package javaCode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import java.util.Vector;

/**
 * в данном классе демонстрируется работа с разделяемым ресурсом
 */
public class Threads2 extends Thread
{
	static Vector<ResAccessor> owners;
	
	/**
	 * в данном методе создается:<p>
	 * - разделяемый ресурс<p>
	 * - объект, следящий за ним<p>
	 * - (c задержкой в 0.5 сек) 5 работников с разделяемым ресурсом 
	 */
	public static void main(String[] args)
	{
		new Poller();
		owners = new Vector<ResAccessor>();
		for(int i = 0; i < 5; i++)
		{
			owners.addElement(new ResAccessor());
		}
		for(ResAccessor o : owners)
		{
			try
			{
				sleep(500);
			}
			catch(Exception x)
			{
				x.printStackTrace();
			}
			o.start();
		}
	}
	
	static String getTime() 
	{
        return new SimpleDateFormat("mm:ss ").format(new Date());
    }
}

/**
 * объект класса ResAccessor взаимодействует с объектом класса Resource
 */
class ResAccessor extends Thread
{
	Random randomizer;
	String accessorName;
	Integer lifeTime;
	public ResAccessor()
	{
		randomizer = new Random();
		accessorName = String.valueOf(randomizer.nextInt(500));
		lifeTime = 5 + randomizer.nextInt(5); 
		setName(accessorName);							// имена потоков (они еще и в стекфрейме во время дебага будут видны)
		System.out.println(Threads2.getTime() + "ResAccessor.ResAccessor()" + " : создан объект : " + accessorName + " со временем жизни " + lifeTime + " секунд");
	}
	
	@Override
	public void run()
	{
		try
		{
			open();										// занимаем ресурс
			work();										// работаем 
			close();									// освобождаем ресурс
		}
		catch(Exception x)
		{
			x.printStackTrace();
		}
	}

	void open() throws Exception
	{
		synchronized (Resource.getInstance())
		{
			if (Resource.getInstance().getState())  
			{
				for(;;)
				{
					wait();								// ждем сообщения notify() от других потоков
				}
			}
			Resource.getInstance().setOwner(ResAccessor.this);
		}
	}
	
	void close()
	{
		synchronized (Resource.getInstance())
		{
			
			Resource.getInstance().release();
			Threads2.owners.remove(ResAccessor.this);
			for(ResAccessor o : Threads2.owners)
				o.notify();
		}
	}
	
	void work() throws Exception
	{
		sleep(1000 * ResAccessor.this.lifeTime);
	}
}

/**
 * синглетон "ресурс"
 */
class Resource
{
	private boolean isBusy;			// переменная состояния ресурса
	private ResAccessor user;		// ссылка на сторонний объект, занявший данный ресурс 
	private static Resource ptr;	// ссылка на себя, статик обеспечивает единственность экземпляра ресурса

	static Resource getInstance()
	{
		if (ptr == null)
			ptr = new Resource();
		return ptr;
	}

	/**
	 * освобождает ресурс
	 */
	void release()
	{
		user = null;
		isBusy = false;
	}
	
	/**
	 * продает в рабство ресурс 
	 */
	void setOwner(ResAccessor owner)
	{
		user = owner;
		isBusy = true;
	}
	
	/**
	 * возвращает хозяина ресурса 
	 */
	ResAccessor getOwner()
	{
		return user;
	}

	/**
	 * возвращает занят/не занят ресурс
	 */
	boolean getState()
	{
		return isBusy;
	}
	
	private Resource()
	{
		isBusy = false;
	}

}

/**
 * каждые 0.5 сек. отслеживает:
 *  - занятость ресурса
 *  - имя объекта, занявшего его
 *  - имена объектов, ждущих своей очереди
 */
class Poller extends Thread
{
	boolean oldStatusOfResource;
	boolean currentStatusOfResource;
	
	static String changed = "изменился";
	static String unchanged = "не изменился";
	static String busy = "занят";
	static String free = "свободен";
	
	String status;			// статус: изменился/не изменился объект на момент итерации проверки
	String state;			// состояние: занят/свободен объект на момент итерации проверки
	Vector<String> names;	// имена всех существующих возможных владельцев, один из которых на данный момент может являться владельцем ресурса

	public Poller()
	{
		names = new Vector<String>();
		start();
	}

	@Override
	public void run()
	{
		for (;;)
		{
			for(ResAccessor o : Threads2.owners)
			{
				names.add(o.accessorName);
			}
			System.out.print(Threads2.getTime() + "Poller.run()" + " : возможные владельцы объекта: ");
			for(ResAccessor o : Threads2.owners)
			{
				System.out.print(o.accessorName + ";");
			}
			System.out.println();
			
			currentStatusOfResource = Resource.getInstance().getState();
			String status = (oldStatusOfResource == currentStatusOfResource ? unchanged : changed);
			String state = (currentStatusOfResource == true ? busy : free);
			if(status.equals(changed))
			{
				System.out.println(Threads2.getTime() + "Poller.run()" + " : состояние объекта изменилось");
			}
			System.out.println(Threads2.getTime() + "Poller.run()" + " : текущее состояние объекта: " + state);
			System.out.println(Threads2.getTime() + "Poller.run()" + " : текущий владелец объекта: " + (Resource.getInstance().getOwner() == null ? "отсутствует" : Resource.getInstance().getOwner().accessorName));
			oldStatusOfResource = currentStatusOfResource;
			try
			{
				sleep(500);
			} 
			catch (Exception x)
			{
				x.printStackTrace();
			}
		}
	}
}